﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace FileRenamer
{
	public class Expression
	{
		public String Input;
		public SpecialChars Special = new SpecialChars('\\', '{', '}');

		public Expression()
		{
		}
		public Expression(String input) : this()
		{
			this.Input = input;
		}

		public String Parse()
		{
			return ParseText(this.Input, this.Special);
		}

		public static String Parse(String input, SpecialChars special)
		{
			return ParseText(input, special);
		}

		private static string ParseText(String input, SpecialChars special)
		{
			try
			{
				//if (!ParseSeparators(input, special)) 
				//	throw new Exception("Bracket mismatch.", new Exception("An open bracket cannot come before a close bracket, and there cannot be two open or close brackets in a row."));

				CodeBlock codeBlock = new CodeBlock(input);
				List<String> strings = new List<String>();

				//if (parts[0].Type == PartType.CodeBlock && parts[0].Text[1] == ':') 
				//	throw new Exception("A codeblock cannot start with ':' and be first item.", new Exception("If a codeblock starts with a ':' then it modifies the text directly before it"));

				//int delta = 0;
				//for (int i = 0; i < parts.Count; i++)
				//{
				//  if (parts[i].Type == PartType.TextBlock)
				//  {
				//    parts[i].Text = parts[i].Text.Replace(special.Escape.ToString(), "");
				//    strings.Add(parts[i].Text);
				//  }
				//  else if (parts[i].Type == PartType.CodeBlock && parts[i].Text[1] != ':')
				//  {
				//    Operation op = new Operation(parts[i].Text.Substring(1, parts[i].Text.IndexOf(':') - 1),
				//                                 parts[i].Text.Substring(parts[i].Text.IndexOf(':') + 1).Replace(special.Close.ToString(), ""));
				//    strings.Add(op.Output);
				//  }
				//  else if (parts[i].Type == PartType.CodeBlock && parts[i].Text[1] == ':')
				//  {
				//    Operation op = new Operation(strings[i - 1 - delta], parts[i].Text.Substring(parts[i].Text.IndexOf(':') + 1).Replace(special.Close.ToString(), ""));
				//    strings.RemoveAt(i - 1 - delta);
				//    delta++;
				//    strings.Add(op.Output);
				//  }
				//}

				string output = "";
				foreach(String s in strings)
				{
					output += s;
				}
				output = new Operation(codeBlock).Output;

				return output;
			}
			catch (Exception e)
			{
				return "invalidText";
				//return "Message: " + e.Message + "\n Inner: " + e.InnerException;
			}
		}

		#region CommandBuilder
		public class CommandBuilder
		{
			

			public CommandBuilder(CodeBlock codeBlock)
			{
				
			}
		}
		#endregion

		#region CodeBlock Class
		public class CodeBlock
		{
			public String Block;
			public List<CodeBlock> Children = new List<CodeBlock>();
			public CodeBlock Parent;
			public String Output;

			public CodeBlock(string block)
			{
				Block = block;
				Parent = null;
				Parse();
			}
			private CodeBlock(string block, ref CodeBlock parent)
			{
				Block = block;
				Parent = parent;
				Parse();
			}

			public void Parse()
			{
				int opens = 0;				//stores the number of open brackets found;
				int closes = 0;				//stores the number close brackets found;

				String text = Block;	//create a copy of the input text to search;
				int startPos = 0;			//this variable is used to store the position of the current child;
				int current = -1;			//stores a check to stop any infinite looping;
				for (int i = 0; i < text.Length; i++)
				{
					if (text[i] == '{') opens++;
					else if (text[i] == '}') closes++;
					///	only continue if the number of open and close brackets is equal
					///	dont contiue if opens = 0; I did this to stop if from adding an empty child (example text: "abc{1})"
					/// after one child has been added the check {opens > 0} will not work anymore so I had to add {current != (opens + closes)}
					if (opens == closes && opens > 0 && current != (opens + closes))
					{
						///	if it found a sequence that should be added as a child then create a new child
						/// the substring should start where the previous child left off; if it is the first child then start at 1 (omitting the first bracket)
						/// the length should be where it found the closing bracket that made the count match; then subtract the startPos from that;
						Children.Add(new CodeBlock(text.Substring(startPos + 1, i - 1 - startPos)));
						startPos = i + 1;					//set the new start position; add 1 to omit the opening bracket (if left on then you will get empty children and a possible infinite loop
						current = opens + closes;	//get the current count of opening and closing brackets as to not added this one again in the next iteration;
					}
				}

				if (opens != closes)
					throw new Exception("Brackets count did not match", new Exception("The number of open brackets did not equal the number of close brackets. Could not parse Expression."));

			}

			public override string ToString()
			{
				return "Block = " + Block + "|Count = " + Children.Count;
			}
		}
		#endregion

		#region Split into parts
		//private static List<Part> SplitIntoPieces(String pattern, SpecialChars special)
		//{
		//  List<string> parts = new List<string>();

		//  Part part = new Part();
		//  bool started = true;
		//  char first = pattern[0];
		//  int startIndex = 0, endIndex = -1;
		//  part.Type = first == special.Open ? PartType.CodeBlock : PartType.TextBlock;

		//  for(int i = 1; i < pattern.Length; i++)
		//  {
		//    if (!started)
		//    {
		//      first = pattern[i];
		//      part.Type = first == special.Open ? PartType.CodeBlock : PartType.TextBlock;
		//      started = true;
		//      startIndex = i;
		//    }
		//    else
		//    {
		//      if (part.Type == PartType.CodeBlock)
		//      {
		//        if (pattern[i] == special.Close && pattern[i-1] != special.Escape)
		//        {
		//          endIndex = i;
		//          if (endIndex + 1 < pattern.Length)
		//            part.Text = pattern.Substring(startIndex, endIndex + 1 - startIndex);
		//          else
		//            part.Text = pattern.Substring(startIndex);
		//          parts.Add(part);
		//          part = new Part();
		//          started = false;
		//        }
		//      }
		//      else if (part.Type == PartType.TextBlock)
		//      {
		//        if (pattern[i] == special.Open && pattern[i - 1] != special.Escape)
		//        {
		//          endIndex = i;
		//          part.Text = pattern.Substring(startIndex, endIndex - startIndex);
		//          parts.Add(part);
		//          part = new Part();
		//          part.Type = PartType.CodeBlock;
		//          startIndex = i;
		//        }
		//      }
		//    }
		//  }
		//  if (part.Type == PartType.TextBlock)
		//  {
		//    part.Text = pattern.Substring(startIndex);
		//    parts.Add(part);
		//  }
		//  return parts;
		//}
		#endregion

		#region Parse separators
		//private static bool ParseSeparators(String text, SpecialChars special)
		//{
		//  String chars = "";
		//  if (text[0] == special.Open) chars += text[0];
		//  else if (text[0] == special.Close) return false;
		//  for (int i = 1; i < text.Length; i++)
		//  {
		//    if ((text[i] == special.Open || text[i] == special.Close) && text[i - 1] != special.Escape) chars += text[i];
		//  }

		//  for (int i = 0; i < chars.Length; i++)
		//  {
		//    if (i % 2 == 0)
		//    {
		//      if (chars[i] != special.Open) return false;
		//    }
		//    else if (i % 2 == 1) if (chars[i] != special.Close) return false;
		//  }

		//  return true;
		//}
		#endregion

		#region Operation class
		//private class Operation
		//{
		//  private string _commandText;
		//  public String CommandText
		//  {
		//    get { return _commandText; }
		//    set
		//    {
		//      CommandRef = value.Substring(0, value.IndexOf("("));
		//      _commandText = value.Substring(CommandRef.Length + 1, value.Length - CommandRef.Length - 2);
		//    }
		//  }
		//  public String CommandRef
		//  {
		//    get;
		//    private set;
		//  }
		//  public CodeBlock CodeBlock;
		//  public String Input;
		//  public String Output;
		//  public Commands commands = new Commands();
		//  public Operation()
		//  {
		//  }
		//  public Operation(CodeBlock codeBlock)
		//  {
		//    ParseOperator();
		//  }
		//  public bool VerifyCommand()
		//  {
		//    if (commands.Find(e => e.commandRef == CommandRef) == null) return false;
		//    return true;
		//  }

		//  public void ParseOperator()
		//  {
		//    if (!VerifyCommand()) throw new Exception("Could not parse the command. The specified command was not valid");

		//    Output = GetEvaluation(CodeBlock);
		//  }
		//  private string GetEvaluation(CodeBlock code)
		//  {
		//    string output = "";
		//    if (code.Children.Count == 0) output = (string)commands.Find(e => e.commandRef == CommandRef).action.DynamicInvoke(Input, CommandText);
		//    else
		//    {
		//      for (int i = 0; i < code.Children.Count; i++)
		//      {
		//        output += new Operation(code.Children[i]).Output;
		//      }
		//    }
		//    return output;
		//  }
		//}
		#endregion

		#region Command class
		public class Commands : List<Command>
		{
			public Commands()
			{
				this.Add(new Command("ins", new dInsert(InsertText)));
				this.Add(new Command("rep", new dReplace(ReplaceText)));
				this.Add(new Command("rem", new dRemove(RemoveText)));
				this.Add(new Command("rem", new dCasing(Casing)));
			}

			private String[] GetArguements(int pos, string commandTExt)
			{
				return null;
			}

			#region delegates
			#region Remove
			public delegate string dCasing(string input, string commandText);
			public String Casing(string input, string commandText)
			{
				String[] args = commandText.Split(',');
				return "not imp";
			}
			#endregion

			#region Remove
			public delegate string dRemove(string input, string commandText);
			public String RemoveText(string input, string commandText)
			{
				String[] args = commandText.Split(',');
				char direction;
				int count = 0;
				if (ParseInt(args[0], out direction, out count))
				{
					if (args.Length > 1)
					{
						int startIndex = 0;
						if (int.TryParse(args[1], out startIndex))
						{
							if (direction == '-') return input.Substring(0, startIndex + count) + input.Substring(startIndex);
							else return input.Remove(startIndex, count);
						}
						throw new Exception("Could not parse the second arguement of the rem command", new Exception("Looking for int but found something else."));

					}
					else
					{
						if (direction == '-') return input.Substring(0, input.Length + count);
						else return input.Remove(0, count);
					}
				}
				throw new Exception("Could not parse the first arguement of the rem command", new Exception("Looking for int but found something else."));
			}
			#endregion

			#region Replace
			public delegate string dReplace(string input, string commandText);
			public String ReplaceText(string input, string commandText)
			{
				String[] args = commandText.Split(',');
				char mode = args[0][0].ToString().ToLower().Trim()[0];
				string replaceText = args[1];
				string newText = args[2];
				bool matchCase = true;
				if (args.Length > 3) matchCase = args[3].Trim().ToLower().StartsWith("c");

				if (input.IndexOf(replaceText, matchCase ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase) != -1)
				{
					if (mode == 'a')
					{
						return Regex.Replace(input, replaceText, newText, matchCase ? RegexOptions.None : RegexOptions.IgnoreCase);
					}
					if (mode == 'l')
					{
						int oldTextStart = input.LastIndexOf(replaceText, matchCase ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase);
						String beginning = input.Substring(0, oldTextStart);
						String end = input.Substring(oldTextStart + replaceText.Length);
						return beginning + newText + end;
					}
					else //mode = first only
					{
						return input.Substring(0, input.IndexOf(replaceText, matchCase ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase)) + newText +
							input.Substring(input.IndexOf(replaceText, matchCase ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase) + replaceText.Length);
					}
				}
				else
				{
					return input;
				}
			}
			#endregion

			#region Insert
			private delegate string dInsert(string input, string commandText);
			private String InsertText(string input, string commandText)
			{
				String[] args = commandText.Split(',');
				int pos = -1;
				char direction;
				if (!ParseInt(args[0], out direction, out pos))
					throw new Exception("Could not parse the first arguement of the 'ins' command.", new Exception("The type should be of integer."));

				string insertText = args[1];

				if (direction == '-')
					return input.Insert(input.Length - -pos, insertText);
				else
					return input.Insert(pos, insertText);
			}

			#endregion
			#endregion

			private bool ParseInt(string text, out char direction, out int value)
			{
				if (text[0] == '-') direction = '-';
				else direction = '+';
				return int.TryParse(text, out value);
			}
		}
		public class Command
		{
			public String commandRef;
			public Delegate action;
			public Command()
			{
			}
			public Command(String text, Delegate action)
			{
				this.commandRef = text;
				this.action = action;
			}
		}
		#endregion

		#region Part Class
		//private class Part
		//{
		//  public String Text;
		//  public PartType Type;

		//  public Part()
		//  {
		//    Type = PartType.None;
		//  }

		//  public Part(String text, PartType type)
		//  {
		//    Text = text;
		//    Type = type;
		//  }

		//  public new String ToString()
		//  {
		//    return Text.ToString() + ":" + Type.ToString();
		//  }
		//}

		//private enum PartType
		//{
		//  TextBlock, CodeBlock, None
		//}
		#endregion

		#region Separator class
		public class SpecialChars
		{
			public char Escape, Open, Close;

			public SpecialChars()
			{
				Escape = '\\';
				Open = '{';
				Close = '}';
			}

			public SpecialChars(char escapeChar, char openChar, char closeChar)
			{
				Escape = escapeChar;
				Open = openChar;
				Close = closeChar;

				if (Escape == Open || Escape == Close || Open == Close) throw new Exception("The escape, open and close characters must be different.", new Exception(""));
			}
			public bool IsOpenOrClose(char c)
			{
				return (c == Open || c == Close);
			}
		}
		#endregion

	}
}
